package plgutils

import (
	"dgo/MyProj/PrjHttpUtils"
	"dgo/goutils/utils"
	"dgo/goutils/yhttp"
	"encoding/base64"
	"fmt"
	"gitee.com/ymofen/supervalue"
	"strings"
	"time"
)

type PlgUtilAppVars struct {
	PlgBase
	msglst            *utils.SyncCycleList
	lst               utils.GSyncMapGroup
	lst_varlst        utils.GSyncMapGroup
	varlsturl         string
	varlstprefix      string
	varlstval         string
	varlstlastchanged time.Time
	reloadTimer       *utils.Timer
}

func (this *PlgUtilAppVars) clean() {
	this.lst.Range(func(key, val interface{}) bool {
		if itm, ok := val.(*VarFromURLItem); ok {
			itm.owner = nil
			utils.UnRegisterFactoryVar(itm.key)
		} else {
			utils.UnRegisterFactoryVar(val)
		}
		this.lst.Remove(key)
		return true
	})
}

func (this *PlgUtilAppVars) registerVarlst(rawstr string) {
	vallst := strings.Split(rawstr, "\n")
	for i := 0; i < len(vallst); i++ {
		str := vallst[i]
		key, val := utils.Split2Str(str, ":")
		if len(key) > 0 {
			if len(this.varlstprefix) > 0 {
				key = fmt.Sprintf("%s%s", this.varlstprefix, key)
			}

			buf, err := base64.RawURLEncoding.DecodeString(val)
			if err != nil {
				this.msglst.LogMsg("varlst decode err, key:%s, err:%s", key, err.Error())
			} else {
				val = string(buf)
				utils.RegisterFactoryVar(key, val)
			}
			this.lst.Set(key, &this.lst_varlst)
			this.lst_varlst.Set(key, 1)
		}
	}

	this.lst_varlst.Range(func(key, value interface{}) bool {
		if value == 0 { // 不存在，删除
			this.lst_varlst.Remove(key)
			utils.UnRegisterFactoryVar(key)
			this.lst.Remove(key)
		} else {
			this.lst_varlst.Set(key, 0)
		}
		return true
	})
}

func (this *PlgUtilAppVars) reloadVarlst() {
	if len(this.varlsturl) == 0 {
		return
	}

	err, rawstr := GetUrlVarRawValue(this.varlsturl)
	if err != nil {
		// 出错不处理
		this.msglst.LogMsg("varlst reload err, url:%s, err:%s", this.varlsturl, err.Error())
		return
	}
	if this.varlstval != rawstr {
		this.varlstval = rawstr
		this.varlstlastchanged = time.Now()
		this.msglst.LogMsg("varlst val has changed:%s", rawstr)
		this.registerVarlst(rawstr)
	}
}

/*
*

	{
	   "id":"area-vars",
	   "type":"utils.app.vars",
	   "conf":{
	      "vars": {
	        "redis":"119.96.165.202:56379"
	      },
	      "varsfromurl": {
	         "alg-switch": "http://127.0.0.1:16106/dbconf/get?id=alg-switch-corsplus&type=vrs"
	      },
	      "varlst-url":"http://119.96.80.54:15015/varlst/get?type=vrs&encode=3&kvsep=:&rawflag=1",
	      "varlst-prefix":"",
	      "varsfromurl-reload-interval-secs":10
	   }
	}

varsfromurl:

	可以返回json值:{"value":""}, 也可以返回整个的字符串(非JSON格式)

varsfromurl-reload-interval-secs: 10 默认10秒重载一次
*/
func (this *PlgUtilAppVars) ReloadConf(conf *wrapper.SuperValue) error {
	if conf == nil {
		return nil
	}

	if this.msglst == nil {
		this.msglst = utils.NewSyncCycleList()
		this.msglst.ConfigMaxSize(1024)
	}

	this.clean()
	vars := conf.ValueByName("vars")
	if vars != nil {
		vars.Range(func(key string, val *wrapper.SuperValue) bool {
			utils.RegisterFactoryVar(key, val.String())
			this.lst.Set(key, val.String())
			return true
		})
	}

	varsfromurl := conf.ValueByName("varsfromurl")
	if varsfromurl != nil {
		varsfromurl.Range(func(key string, val *wrapper.SuperValue) bool {
			itm := NewVarFromURLItem(this)
			itm.key = key
			itm.url = val.String()
			if itm.Reload() {
				utils.RegisterFactoryVar(key, itm.val)
				this.lst.Set(key, itm)
			}
			return true
		})
	}

	this.varlsturl = conf.StringByName("varlst-url", "")
	conf.CheckExistsStrFunc("varlst-prefix", func(strv string) {
		if this.varlstprefix != strv {
			this.varlstprefix = strv
			this.varlstval = ""
		}
	})

	this.reloadVarlst()

	if this.reloadTimer == nil {
		this.reloadTimer = utils.NewTimer()
		this.reloadTimer.OnTimer = func(tag interface{}) {
			this.lst.Range(func(key, value interface{}) bool {
				if varItm, ok := value.(*VarFromURLItem); ok {
					if varItm.Reload() {
						utils.RegisterFactoryVar(varItm.key, varItm.val)
						this.lst.Set(varItm.key, varItm)
					}
				}
				return true
			})
			this.reloadVarlst()
		}
	}
	this.reloadTimer.ConfigInterval(time.Duration(conf.Int64ByName("varsfromurl-reload-interval-secs", 10))*time.Second, 0)

	return nil
}

func (this *PlgUtilAppVars) OnObjectNotify(sender interface{}, notifytype int, data interface{}) (rval interface{}, err error) {
	if notifytype == utils.NOTIFY_TYPE_SETCONF {
		if conf, ok := data.(*wrapper.SuperValue); ok {
			err = this.ReloadConf(conf)
		} else {
			err = fmt.Errorf("非法的JSON配置")
		}
	} else if notifytype == utils.NOTIFY_TYPE_RELEASE {
		this.ReleaseFlag = 1
		this.clean()
	}
	return
}

func CreatePlgUtilAppVarsFunc(args ...interface{}) (rval interface{}, err error) {
	obj := &PlgUtilAppVars{}
	if len(args) > 0 {
		if str, ok := args[0].(string); ok {
			obj.PlugId = str
		}
	}
	rval = obj
	return
}

func (this *PlgUtilAppVars) OnHttpStatus(rec *PlgHttpRec) (rval interface{}, errcode int, err error) {
	itype := rec.QryAsInt("type", 0)
	if itype == 99 {
		json := wrapper.NewSVObject()
		json.SetKeyValues("now", utils.NowString())
		PrjHttpUtils.JSON2TableRec(rec.RespW, "", json.AsJSONString(false))
	} else if itype == 98 {
		rec.ResponseSyncListNEx(this.msglst)
	} else if itype == 1 {
		searchval := rec.QryAsString("searchval", "")
		json := wrapper.NewSVArray()
		utils.RangeFactoryVarsFuncEx(1, func(key string, value interface{}) bool {
			ok := true
			if len(searchval) > 0 {
				ok = strings.Contains(key, searchval) || strings.Contains(fmt.Sprintf("%v", value), searchval)
			}
			return ok
		}, func(k0 string, v0 interface{}, k1 string, v1 interface{}) bool {
			return k0 < k1
		}, func(key string, val interface{}) bool {
			itm := json.AppendObject()
			itm.SetKeyValue("key", key)
			itm.SetKeyValue("value", fmt.Sprintf("%v", val))
			return true
		})
		if rec.QryAsInt("json", 0) == 1 {
			PrjHttpUtils.ResponseAsJSON(rec.RespW, json.AsJSONStringEx(false, false))
		} else {
			PrjHttpUtils.JSON2Table(rec.RespW, "", json.AsJSONString(false))
		}
	} else if itype == 2 {
		searchval := rec.QryAsString("searchval", "")
		json := wrapper.NewSVArray()
		this.lst.ListRangeV3(1, 1, func(key, value interface{}) bool {
			ok := true
			if len(searchval) > 0 {
				ok = strings.Contains(key.(string), searchval)
			}
			return ok
		}, func(k0, v0, k1, v1 interface{}) bool {
			return k0.(string) < k1.(string)
		}, func(key, val interface{}) bool {
			itm := json.AppendObject()
			itm.SetKeyValue("key", key)
			if varItm, ok := val.(*VarFromURLItem); ok {
				itm.SetKeyValue("register-val", varItm.url)
				itm.SetKeyValue("uptime", utils.DateTimeString2(varItm.uptime))
			} else if lst, ok := val.(*utils.GSyncMapGroup); ok {
				if lst == &this.lst_varlst {
					itm.SetKeyValue("register-val", this.varlsturl)
					itm.SetKeyValue("uptime", utils.DateTimeString2(this.varlstlastchanged))
				}
			} else {
				itm.SetKeyValue("register-val", val)
				itm.SetKeyValue("reloadTime", "")
			}
			_, v := utils.InvokeFactoryVarFunc(key, nil)
			itm.SetKeyValue("final-val", v)

			return true
		})
		if rec.QryAsInt("json", 0) == 1 {
			PrjHttpUtils.ResponseAsJSON(rec.RespW, json.AsJSONStringEx(false, false))
		} else {
			PrjHttpUtils.JSON2Table(rec.RespW, "", json.AsJSONString(false))
		}
	} else {
		var sb utils.BytesBuilder
		PrjHttpUtils.AppendIndexBody(&sb, "status", "status?type=99")
		PrjHttpUtils.AppendIndexBody(&sb, "msg-list", "status?type=98&searchval=")
		PrjHttpUtils.AppendIndexBody(&sb, "register-list", "status?type=2&searchval=")
		PrjHttpUtils.AppendIndexBody(&sb, "vars-list", "status?type=1&searchval=")
		PrjHttpUtils.ResponseBodyHtml(rec.RespW, sb.String())
	}

	return
}

func GetUrlVarRawValue(url string) (err error, rval string) {
	respBuf, err := yhttp.HttpGet(url)
	if err != nil {
		return err, ""
	}
	if len(respBuf) == 0 {
		return nil, ""
	}
	json, err := wrapper.NewSVFromBuf(respBuf)
	if err != nil {
		tmpstr := string(respBuf)
		if tmpstr == "err" {
			return fmt.Errorf("response err from server"), ""
		}
		if strings.HasPrefix(tmpstr, "err") {
			return fmt.Errorf("%s", tmpstr), ""
		}
		return nil, tmpstr
		//this.LogMsg("[%s]url:%s, val:%v", this.key, this.url, this.val)
	} else {
		if json.IntByName("code", 0) == 0 {
			return nil, json.StringByName("val", "")
		} else {
			return fmt.Errorf("%s", json.StringByName("msg", "")), json.StringByName("val", "")
		}
	}
}

func init() {
	utils.RegisterCreate("utils.app.vars", CreatePlgUtilAppVarsFunc)
}
